package com.mdd.front.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mdd.common.config.GlobalConfig;
import com.mdd.common.core.PageResult;
import com.mdd.common.entity.order.AccountLog;
import com.mdd.common.entity.order.Order;
import com.mdd.common.entity.order.RechargeOrder;
import com.mdd.common.entity.user.PointsLog;
import com.mdd.common.entity.user.User;
import com.mdd.common.entity.user.UserAuth;
import com.mdd.common.enums.AccountChangeEnum;
import com.mdd.common.enums.ClientEnum;
import com.mdd.common.enums.NoticeEnum;
import com.mdd.common.enums.PointsChangeEnum;
import com.mdd.common.exception.OperateException;
import com.mdd.common.mapper.order.AccountLogMapper;
import com.mdd.common.mapper.order.OrderMapper;
import com.mdd.common.mapper.order.RechargeOrderMapper;
import com.mdd.common.mapper.user.PointsLogMapper;
import com.mdd.common.mapper.user.UserAuthMapper;
import com.mdd.common.mapper.user.UserMapper;
import com.mdd.common.utils.*;
import com.mdd.front.LikeFrontThreadLocal;
import com.mdd.front.config.FrontConfig;
import com.mdd.front.service.IUserService;
import com.mdd.front.validate.NewUserUpdateValidate;
import com.mdd.front.validate.PageParam;
import com.mdd.front.validate.UserPointsUpdateParam;
import com.mdd.front.vo.course.CourseListVo;
import com.mdd.front.vo.user.UserCenterVo;
import com.mdd.front.vo.user.UserInfoVo;
import com.mdd.front.vo.user.UserWalletFlowVo;
import com.mdd.front.vo.user.UserWalletVo;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.*;

/**
 * 用户服务实现类
 */
@Service
public class UserServiceImpl implements IUserService {

    @Resource
    UserMapper userMapper;

    @Resource
    UserAuthMapper userAuthMapper;

    @Resource
    AccountLogMapper accountLogMapper;

    @Resource
    PointsLogMapper pointsLogMapper;
    /**
     * 个人中心
     *
     * @author fzr
     * @param userId 用户ID
     * @return UserCenterVo
     */
    @Override
    public UserCenterVo center(Integer userId, String terminal) {
        User user = userMapper.selectOne(new QueryWrapper<User>()
                .select("id,sn,avatar,real_name,nickname,account,mobile, create_time")
                .eq("id", userId)
                .last("limit 1"));

        UserAuth userAuth = userAuthMapper.selectOne(new QueryWrapper<UserAuth>()
                .select("id,openid")
                .eq("user_id", userId)
                .eq("terminal", terminal)
                .last("limit 1"));

        UserCenterVo vo = new UserCenterVo();
        BeanUtils.copyProperties(user, vo);
        if (user.getAvatar().equals("")) {
            String avatar = ConfigUtil.get("user", "defaultAvatar", "");
            vo.setAvatar(UrlUtil.toAbsoluteUrl(avatar));
        } else {
            vo.setAvatar(UrlUtil.toAbsoluteUrl(user.getAvatar()));
        }
        vo.setIsBindMnp(userAuth != null);
        vo.setRegTime(TimeUtil.timestampToDate(user.getCreateTime()));
        return vo;
    }

    /**
     * 个人中心
     *
     * @author fzr
     * @param userId 用户ID
     * @return UserCenterVo
     */
    @Override
    public UserWalletVo wallet(Integer userId) {
        User user = userMapper.selectOne(new QueryWrapper<User>()
                .select("id,user_money")
                .eq("id", userId)
                .last("limit 1"));

        UserWalletVo vo = new UserWalletVo();
        vo.setId(user.getId());
        vo.setBalance(user.getUserMoney());
        Map<String, String> rechargeConfig = LsConfigUtil.get("recharge");
        vo.setRechargeConfig(rechargeConfig);
        return vo;
    }

    private String changeTypeMsg(int changeType) {
        if (changeType == AccountChangeEnum.BNW_DEC_ADMIN.getCode()) {
            return AccountChangeEnum.BNW_DEC_ADMIN.getMsg();
        } else if (changeType == AccountChangeEnum.BNW_DEC_ORDER.getCode()) {
            return AccountChangeEnum.BNW_DEC_ORDER.getMsg();
        } else if (changeType == AccountChangeEnum.BNW_INC_REFUND.getCode()) {
            return AccountChangeEnum.BNW_INC_REFUND.getMsg();
        } else if (changeType == AccountChangeEnum.BNW_INC_ADMIN.getCode()) {
            return AccountChangeEnum.BNW_INC_ADMIN.getMsg();
        } else if (changeType == AccountChangeEnum.BNW_INC_RECHARGE.getCode()) {
            return AccountChangeEnum.BNW_INC_RECHARGE.getMsg();
        } else {
            return AccountChangeEnum.BNW_INC_RECHARGE_GIVE.getMsg();
        }
    }

    private String getActionTag(int changeType) {
        if (changeType == AccountChangeEnum.BNW_DEC_ADMIN.getCode()) {
            return "-";
        } else if (changeType == AccountChangeEnum.BNW_DEC_ORDER.getCode()) {
            return "-";
        } else if (changeType == AccountChangeEnum.BNW_INC_REFUND.getCode()) {
            return "+";
        } else if (changeType == AccountChangeEnum.BNW_INC_ADMIN.getCode()) {
            return "+";
        } else if (changeType == AccountChangeEnum.BNW_INC_RECHARGE.getCode()) {
            return "+";
        } else {
            return "+";
        }
    }

    @Override
    public PageResult<UserWalletFlowVo> getUserWalletFlow(PageParam pageParam, Integer action, Integer userId) {
        Integer page  = pageParam.getPageNo();
        Integer limit = pageParam.getPageSize();
        action = action != null ? action : -1;
        QueryWrapper<AccountLog> queryWrapper = new QueryWrapper<AccountLog>()
                .select("id,change_type, change_amount, update_time")
                .eq("user_id", userId)
                .isNull("delete_time")
                .orderByDesc("update_time");
        if (action.intValue() == AccountChangeEnum.ACTION_INC.getCode()) {
            queryWrapper.in("change_type", AccountChangeEnum.BNW_INC_ADMIN.getCode(), AccountChangeEnum.BNW_INC_RECHARGE.getCode(), AccountChangeEnum.BNW_INC_REFUND.getCode(), AccountChangeEnum.BNW_INC_RECHARGE_GIVE.getCode());
        } else if (action.intValue() == AccountChangeEnum.ACTION_DEC.getCode()) {
            queryWrapper.in("change_type", AccountChangeEnum.BNW_DEC_ADMIN.getCode(), AccountChangeEnum.BNW_DEC_ORDER.getCode());
        }
        IPage<AccountLog> iPage = accountLogMapper.selectPage(new Page<>(page, limit), queryWrapper);
        List<UserWalletFlowVo> vos = new LinkedList<>();
        iPage.getRecords().forEach(item -> {
            int changetType = item.getChangeType();
            UserWalletFlowVo userWalletFlowVo = new UserWalletFlowVo();
            userWalletFlowVo.setAction(changeTypeMsg(changetType));
            userWalletFlowVo.setAmount(getActionTag(changetType) + item.getChangeAmount().toString());
            userWalletFlowVo.setUpdateTime(TimeUtil.timestampToDate(item.getUpdateTime()));
            vos.add(userWalletFlowVo);
        });
        return PageResult.iPageHandle(iPage.getTotal(), iPage.getCurrent(), iPage.getSize(), vos);
    }

    /**
     * 个人信息
     *
     * @author fzr
     * @param userId 用户ID
     * @return UserInfoVo
     */
    @Override
    public UserInfoVo info(Integer userId, String terminal) {
        User user = userMapper.selectOne(new QueryWrapper<User>()
                .select("id,sn,avatar,real_name,nickname,account,mobile,password,gender,create_time")
                .eq("id", userId)
                .last("limit 1"));

        UserAuth userAuth = userAuthMapper.selectOne(new QueryWrapper<UserAuth>()
                .select("id,openid")
                .eq("user_id", userId)
                .eq("terminal", terminal)
                .last("limit 1"));

        UserInfoVo vo = new UserInfoVo();
        BeanUtils.copyProperties(user, vo);
        vo.setAccount(user.getAccount());
        vo.setIsPassword(StringUtil.isNotEmpty(user.getPassword()));
        vo.setIsBindMnp(userAuth != null);
        vo.setVersion(GlobalConfig.version);
        vo.setGender(user.getGender());
        vo.setOpenid(userAuth != null ? userAuth.getOpenid() : null);
        vo.setCreateTime(TimeUtil.timestampToDate(user.getCreateTime()));

        if (StringUtil.isNotEmpty(user.getAvatar())) {
            vo.setAvatar(UrlUtil.toAbsoluteUrl(user.getAvatar()));
        } else {
            String avatar = ConfigUtil.get("user", "defaultAvatar", "");
            vo.setAvatar(UrlUtil.toAbsoluteUrl(avatar));
        }

        return vo;
    }

    /**
     * 编辑信息
     *
     * @author fzr
     * @param params 参数
     * @param userId 用户ID
     */
    @Override
    public void edit(Map<String, String> params, Integer userId) {
        String field = params.getOrDefault("field", "").trim();
        String value =  params.getOrDefault("value", "").trim();

        switch (field) {
            case "avatar":
                User avatarUser = new User();
                avatarUser.setId(userId);
                avatarUser.setAvatar(UrlUtil.toRelativeUrl(value));
                avatarUser.setUpdateTime(System.currentTimeMillis() / 1000);
                userMapper.updateById(avatarUser);
                break;
            case "account":
                Assert.isTrue(vaildAccount(value), "账号必须是3-12字母+数字组合!");
                User accountUser = userMapper.selectOne(new QueryWrapper<User>()
                        .select("id,account")
                        .eq("account", value)
                        .last("limit 1"));

                if (StringUtil.isNotNull(accountUser) && !accountUser.getId().equals(userId)) {
                    throw new OperateException("账号已被使用!");
                }

                if (StringUtil.isNotNull(accountUser) && accountUser.getAccount().equals(value)) {
                    throw new OperateException("新账号与旧账号一致,修改失败!");
                }

                User u = new User();
                u.setId(userId);
                u.setAccount(value);
                u.setUpdateTime(System.currentTimeMillis() / 1000);
                userMapper.updateById(u);
                break;
            case "nickname":
                User nicknameUser = new User();
                nicknameUser.setId(userId);
                nicknameUser.setNickname(value);
                nicknameUser.setUpdateTime(System.currentTimeMillis() / 1000);
                userMapper.updateById(nicknameUser);
                break;
            case "gender":
                User sexUser = new User();
                sexUser.setId(userId);
                sexUser.setGender(Integer.parseInt(value));
                sexUser.setUpdateTime(System.currentTimeMillis() / 1000);
                userMapper.updateById(sexUser);
                break;
            default:
                throw new OperateException("不被支持的类型");
        }
    }

    private boolean vaildAccount(String value) {
        String regex = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{3,12}$";
        return value.matches(regex);
    }

    /**
     * 修改密码
     *
     * @author fzr
     * @param password 新密码
     * @param userId 用户ID
     */
    @Override
    public void changePwd(String password, String oldPassword, Integer userId) {
        User user = userMapper.selectOne(new QueryWrapper<User>()
                .select("id,password,salt")
                .eq("id", userId)
                .last("limit 1"));

        Assert.notNull(user, "用户不存在");

        if (user.getPassword() != null && !"".equals(user.getPassword()) && user.getSalt() != null && !"".equals(user.getSalt())) {
            Assert.notNull(oldPassword, "请输入原密码");
            String oldPwd = ToolsUtil.makeMd5(oldPassword.trim() + user.getSalt());
            if (!oldPwd.equals(user.getPassword())) {
                throw new OperateException("原密码不正确!");
            }
        }

        String salt = ToolsUtil.randomString(5);
        String pwd  = ToolsUtil.makeMd5(password.trim() + salt);

        User u = new User();
        u.setId(userId);
        u.setPassword(pwd);
        u.setSalt(salt);
        u.setUpdateTime(System.currentTimeMillis() / 1000);
        userMapper.updateById(u);
    }

    /**
     * 绑定手机
     *
     * @author fzr
     * @param params 参数
     * @param userId 用户ID
     */
    @Override
    public void bindMobile(Map<String, String> params, Integer userId) {
        String type   = params.getOrDefault("type", "");
        String mobile = params.getOrDefault("mobile", "");
        String code   = params.getOrDefault("code", "").toLowerCase();

        // 校验验证码
        int typeCode = type.equals("bind") ? NoticeEnum.SMS_BIND_MOBILE_CODE.getCode() : NoticeEnum.SMS_CHANGE_MOBILE_CODE.getCode() ;
        Object smsCode = RedisUtil.get(GlobalConfig.redisSmsCode+typeCode+":"+mobile);
        if (StringUtil.isNull(smsCode) || !smsCode.toString().equals(code)) {
            throw new OperateException("验证码错误!");
        }

        User user = userMapper.selectOne(new QueryWrapper<User>()
                .select("id,account,mobile")
                .eq("mobile", mobile)
                .last("limit 1"));

        if (StringUtil.isNotNull(user) && user.getId().equals(userId)) {
            throw new OperateException("手机号已被其它账号绑定!");
        }

        User u = new User();
        u.setId(userId);
        u.setMobile(mobile);
        u.setUpdateTime(System.currentTimeMillis() / 1000);
        userMapper.updateById(u);
    }

    /**
     * 微信手机号
     *
     * @author fzr
     * @param code 获取手机号的Code
     */
    @Override
    public void mnpMobile(String code) {
        Map<String, String> config = ConfigUtil.get("mp_channel");
        WxMaService wxMaService = new WxMaServiceImpl();
        WxMaDefaultConfigImpl wxConfig = new WxMaDefaultConfigImpl();
        wxConfig.setSecret(config.getOrDefault("appSecret", ""));
        wxConfig.setAppid(config.getOrDefault("appId", ""));
        wxMaService.setWxMaConfig(wxConfig);
        try {
            WxMaPhoneNumberInfo wxMaPhoneNumberInfo = wxMaService.getUserService().getNewPhoneNoInfo(code);
            Integer userId = LikeFrontThreadLocal.getUserId();
            User user = new User();
            user.setId(userId);
            user.setMobile(wxMaPhoneNumberInfo.getPhoneNumber());
            user.setUpdateTime(System.currentTimeMillis() / 1000);
            userMapper.updateById(user);
        } catch (WxErrorException e) {
            throw new OperateException(e.getError().getErrorCode() + ", " + e.getError().getErrorMsg());
        }
    }

    @Override
    public User getById(Integer id) {
        return this.userMapper.selectById(id);
    }

    @Override
    public int updateById(User user) {
        return this.userMapper.updateById(user);
    }

    @Override
    public Map<String, Object> bindWx(Map<String, String> params, Integer userId) {
        com.baomidou.mybatisplus.core.toolkit.Assert.notNull(params.get("code"), "code参数缺失!");
        String code      = params.get("code");
//        String avatarUrl = params.getOrDefault("avatarUrl", "/api/static/default_avatar.png");
//        String nickName  = params.getOrDefault("nickName", "微信用户");
//        String gender    = params.getOrDefault("gender", "0");
        Integer terminal   = Integer.parseInt(params.getOrDefault("terminal", "1"));
        User user = this.userMapper.selectById(userId);
        Assert.notNull(user, "用户数据不存在");
        try {
            String openId = "";
            String uniId = "";
            if (terminal == ClientEnum.MNP.getCode()) {
                WxMaService wxMaService = WeChatUtil.mnp();

                WxMaJscode2SessionResult sessionResult = wxMaService.getUserService().getSessionInfo(code);
                openId = sessionResult.getOpenid();
                uniId = sessionResult.getUnionid();
            } else {
                WxMpService wxMpService = WeChatUtil.official();
                WxOAuth2AccessToken token = wxMpService.getOAuth2Service().getAccessToken(code);
                openId = token.getOpenId();
                uniId = token.getUnionId();
            }

            String unionId = uniId == null ? "0" : uniId;
            UserAuth userAuth = userAuthMapper.selectOne(new QueryWrapper<UserAuth>()
                    .eq("terminal", terminal).eq("user_id", userId).eq("unionid", unionId).last("limit 1"));
            Assert.isNull(userAuth, "该用户已绑定微信号");

            UserAuth userAuthTemp = userAuthMapper.selectOne(new QueryWrapper<UserAuth>().eq("terminal", terminal).eq("unionid", unionId).last("limit 1"));
            Assert.isNull(userAuthTemp, "该微信已绑定其他账号，不可再绑定当前账号");

            UserAuth auth = new UserAuth();
            auth.setUserId(userId);
            auth.setOpenid(openId);
            auth.setUnionid(unionId.equals("0") ? "" : unionId);
            auth.setTerminal(terminal);
            auth.setCreateTime(System.currentTimeMillis() / 1000);
            auth.setUpdateTime(System.currentTimeMillis() / 1000);
            userAuthMapper.insert(auth);
            String mobile = StringUtil.isNull(user.getMobile()) ? "" : user.getMobile();
            Map<String, Object> response = new LinkedHashMap<>();
            response.put("id", userId);
            response.put("isBindMobile", !mobile.equals(""));
            response.put("isBindWx", true);
            return response;
        } catch (WxErrorException e) {
            throw new OperateException(e.getError().getErrorCode() + ", " + e.getError().getErrorMsg());
        }
    }

    /**
     * 用户积分修改
     *
     * @author panweiliang
     * @param userPointsUpdateParam 参数
     */
    @Override
    public void updateUserPoints(UserPointsUpdateParam userPointsUpdateParam) {

        Integer userId = userPointsUpdateParam.getUserId();
        Integer ownerId = userPointsUpdateParam.getOwnerId();
        User user = userMapper.selectOne(new QueryWrapper<User>()
                .eq("id", userId)
                .last("limit 1"));
        com.baomidou.mybatisplus.core.toolkit.Assert.notNull(user, "用户不存在!");

        user.setUserPoints(userPointsUpdateParam.getUserPoints());
        user.setUpdateTime(TimeUtil.timestamp());

        int result = this.userMapper.updateById(user);
        if (result > 0) {
            PointsLog pointsLog = new PointsLog();
            pointsLog.setUserId(userId);
            pointsLog.setOwnerId(ownerId);
            pointsLog.setChangelessPoints(userPointsUpdateParam.getChangelessPoints());
            pointsLog.setAdditionalPoints(userPointsUpdateParam.getAdditionalPoints());
            pointsLog.setChangePoints(userPointsUpdateParam.getChangePoints());
            pointsLog.setChangeType(PointsChangeEnum.BNW_INC_SIGIN.getCode());
            pointsLog.setRemark(userPointsUpdateParam.getRemark());
            pointsLog.setLeftPoints(userPointsUpdateParam.getUserPoints());
            pointsLog.setAction(PointsChangeEnum.ACTION_INC.getCode());
            pointsLog.setCreateTime(TimeUtil.timestamp());
            pointsLog.setUpdateTime(TimeUtil.timestamp());
            this.pointsLogMapper.insert(pointsLog);
        }

    }

    /**
     * 更新新用户昵称头像等信息
     *
     * @param newUserUpdateValidate 参数
     * @param userId 用户id
     */
    @Override
    public void updateNewUserInfo(NewUserUpdateValidate newUserUpdateValidate, Integer userId) {
        User user = new User();
        user.setId(userId);
        user.setNickname(newUserUpdateValidate.getNickname());
        user.setAvatar(UrlUtils.toRelativeUrl(newUserUpdateValidate.getAvatar()));
        user.setIsNew(0);
        user.setUpdateTime(System.currentTimeMillis() / 1000);
        userMapper.updateById(user);
    }
}
